package remoting

import (
	"bbq/common/logger"
	"bbq/common/protocol/remoteCommand"
	"encoding/binary"
	"github.com/panjf2000/gnet"
	"github.com/panjf2000/gnet/pkg/logging"
	"github.com/panjf2000/gnet/pkg/pool/goroutine"
	"time"
)

/*
  远程通信服务端
*/

type GnetRemoteClient struct {
	base *GnetRemoteBase

	client *gnet.Client

	logger logger.Logger
}

func NewGnetRemoteClient(logger logger.Logger) *GnetRemoteClient {
	return &GnetRemoteClient{
		base:   newGnetRemoteBase(),
		logger: logger,
	}
}

//启动
func (ts *GnetRemoteClient) Start() {
	encoderConfig := gnet.EncoderConfig{
		ByteOrder:                       binary.LittleEndian,
		LengthFieldLength:               4,
		LengthAdjustment:                0,
		LengthIncludesLengthFieldLength: false,
	}
	decoderConfig := gnet.DecoderConfig{
		ByteOrder:           binary.LittleEndian,
		LengthFieldOffset:   0,
		LengthFieldLength:   4,
		LengthAdjustment:    0,
		InitialBytesToStrip: 4,
	}
	codec := gnet.NewLengthFieldBasedFrameCodec(encoderConfig, decoderConfig)

	client, err := gnet.NewClient(ts.base, gnet.WithLogLevel(logging.DebugLevel),
		gnet.WithLockOSThread(true),
		gnet.WithTicker(true),
		gnet.WithCodec(codec))
	ts.client = client
	if err != nil {
		ts.logger.Panicf("%v", err)
		return
	}

	_ = client.Start()
}

/*
 * 注册处理器
 */
func (ts *GnetRemoteClient) RegisterProcessor(code int16, processor GnetRequestProcessor, executor *goroutine.Pool) {
	ts.base.RegisterProcessor(code, processor, executor)
}

//同步请求调用
func (ts *GnetRemoteClient) InvokeSync(addr string, cmd *remoteCommand.RemoteCommand, timeout time.Duration) *remoteCommand.RemoteCommand {
	conn := ts.getConn(addr)
	//构建一个响应future
	opaque := cmd.Header.Opaque
	responseFuture := NewResponseFuture(opaque)
	ts.base.responseTable[opaque] = responseFuture

	err := conn.AsyncWrite(cmd.Encode())
	if err != nil {
		ts.logger.Errorf("invokeSync async write error addr = %s", addr)
	}

	//阻塞等待响应
	respCmd := responseFuture.WaitResponse(timeout)

	return respCmd
}

//根据链接地址获取链接 127.0.0.1:10000
func (ts *GnetRemoteClient) getConn(addr string) gnet.Conn {
	conn, err := ts.client.Dial("tcp", addr)
	if err != nil {
		ts.logger.Errorf("getConn error %v", err)
		return nil
	}
	return conn
}
